﻿/* 
This file is part of Ingo Karstein's Long Path Support project

**Do not remove this comment**

Please see the project homepage at CodePlex:
  http://longpathsupport.codeplex.com/

Please see my blog:
  http://ikarstein.wordpress.com

This code is used in the project "RoboPowerCopy"
  http://robopowercopy.codeplex.com

Licence: GPLv2
    Ingo Karstein's SharePoint 2010 Custom Ribbon Demo
    Copyright (C) 2011  Ingo Karstein, ikarstein@hotmail.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

internal class CopySecurityHelper
{

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern uint GetNamedSecurityInfo(
        string pObjectName,
        SE_OBJECT_TYPE ObjectType,
        SECURITY_INFORMATION SecurityInfo,
        out IntPtr pSidOwner,
        out IntPtr pSidGroup,
        out IntPtr pDacl,
        out IntPtr pSacl,
        out IntPtr pSecurityDescriptor);

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern uint SetNamedSecurityInfo(
        string pObjectName,
        SE_OBJECT_TYPE ObjectType,
        SECURITY_INFORMATION SecurityInfo,
        IntPtr psidOwner,
        IntPtr psidGroup,
        IntPtr pDacl,
        IntPtr pSacl);

    enum SE_OBJECT_TYPE
    {
        SE_UNKNOWN_OBJECT_TYPE = 0,
        SE_FILE_OBJECT,
        SE_SERVICE,
        SE_PRINTER,
        SE_REGISTRY_KEY,
        SE_LMSHARE,
        SE_KERNEL_OBJECT,
        SE_WINDOW_OBJECT,
        SE_DS_OBJECT,
        SE_DS_OBJECT_ALL,
        SE_PROVIDER_DEFINED_OBJECT,
        SE_WMIGUID_OBJECT,
        SE_REGISTRY_WOW64_32KEY
    }

    [Flags]
    enum SECURITY_INFORMATION : uint
    {
        OWNER_SECURITY_INFORMATION = 0x00000001,
        GROUP_SECURITY_INFORMATION = 0x00000002,
        DACL_SECURITY_INFORMATION = 0x00000004,
        SACL_SECURITY_INFORMATION = 0x00000008,
        UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000,
        UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000,
        PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000,
        PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000
    }

    [DllImport("Advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool ConvertStringSidToSid(String StringSid, ref IntPtr Sid);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern IntPtr LocalFree(IntPtr hMem);

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool LookupAccountSid(String lpSystemName, IntPtr Sid, System.Text.StringBuilder lpName, ref int cchName, System.Text.StringBuilder ReferencedDomainName, ref int cchReferencedDomainName, out int peUse);

    public static void SetFileOrFolderOwner(String objectName) //Note this is very basic and is silent on fail as I havent checked GetlastError and thrown an exception etc
    {
        IntPtr sidPtr = IntPtr.Zero;
        SECURITY_INFORMATION sFlags = SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION;

        System.Security.Principal.NTAccount user = new System.Security.Principal.NTAccount("P1R4T3\\Harris");
        System.Security.Principal.SecurityIdentifier sid = (System.Security.Principal.SecurityIdentifier)user.Translate(typeof(System.Security.Principal.SecurityIdentifier));

        ConvertStringSidToSid(sid.ToString(), ref sidPtr);

        SetNamedSecurityInfo(objectName, SE_OBJECT_TYPE.SE_FILE_OBJECT, sFlags, sidPtr, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);

        //Probably should release the IntPtr here to avoid memory leakage?????
    }

    public struct CopySecurityResult
    {
        public int resultCode; //0=OK, 1=Error in Get, 2=Error in Set, 3=Other Error
        public string errorMessageGET;
        public string errorMessageSET;
        public string errorMessageOther;
    }

    public static CopySecurityResult CopySecurity(System.IO.FileInfo src, System.IO.FileInfo dst, bool copyDACL, bool copySACL, bool copyOwner)
    {
        return CopySecurity(src.FullName, dst.FullName, copyDACL, copySACL, copyOwner);
    }
    public static CopySecurityResult CopySecurity(LongFileInfo src, LongFileInfo dst, bool copyDACL, bool copySACL, bool copyOwner)
    {
        return CopySecurity(src.FullQualifiedName, dst.FullQualifiedName, copyDACL, copySACL, copyOwner);
    }
    public static CopySecurityResult CopySecurity(string src, string dst, bool copyDACL, bool copySACL, bool copyOwner)
    {
        CopySecurityResult ret = new CopySecurityResult();

        SECURITY_INFORMATION secInf = 0;
        if( copyDACL )
            secInf = secInf | SECURITY_INFORMATION.GROUP_SECURITY_INFORMATION | SECURITY_INFORMATION.DACL_SECURITY_INFORMATION;
        if( copySACL )
            secInf = secInf | SECURITY_INFORMATION.GROUP_SECURITY_INFORMATION | SECURITY_INFORMATION.SACL_SECURITY_INFORMATION;
        if( copyOwner )
            secInf = secInf | SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION;

        IntPtr pSidGroup, pSidOwner, pDacl, pSacl, pSecurityDescriptor = IntPtr.Zero;
        try
        {
            uint res1 = GetNamedSecurityInfo(src, SE_OBJECT_TYPE.SE_FILE_OBJECT,
                    secInf, out pSidOwner, out pSidGroup, out pDacl, out pSacl, out pSecurityDescriptor);
            if( res1 != 0 )
            {
                string err = new System.ComponentModel.Win32Exception((int)res1).Message;
                ret.resultCode = 1;
                ret.errorMessageOther = err;
            }
            else
            {
                uint res2 = SetNamedSecurityInfo(dst, SE_OBJECT_TYPE.SE_FILE_OBJECT, secInf, pSidOwner, pSidGroup, pDacl, pSacl);
                if( res2 != 0 )
                {
                    string err = new System.ComponentModel.Win32Exception((int)res2).Message;
                    ret.resultCode = 2;
                    ret.errorMessageOther = err;
                }
                else
                {
                    ret.resultCode = 0;
                }
            }
        }
        catch( Exception ex )
        {
            ret.resultCode = 3;
            ret.errorMessageOther = ex.Message;
        }
        finally
        {
            if( pSecurityDescriptor != IntPtr.Zero )
                LocalFree(pSecurityDescriptor);
        }

        return ret;
    }

    public static bool TypeAvailable()
    {
        return true;
    }
}
